#include "box_argument_impl.hh"
#include "../../thirdparty/cxxopts/cxxopts.hpp"
#include "../box/version.hh"
#include "../util/os_util.hh"
#include "../util/string_util.hh"
#include <iostream>
#include <stdexcept>

const char *logo = " _____   _____  __    __\n"
                   "|  _  \\ /  _  \\ \\ \\  / /\n"
                   "| |_| | | | | |  \\ \\/ /\n"
                   "|  _{ | | | | |   }  {\n"
                   "| |_| | | |_| |  / /\\ \\\n"
                   "|_____/ \\_____/ /_/  \\_\\\n\n";

kratos::argument::BoxArgumentImpl::BoxArgumentImpl() {}

kratos::argument::BoxArgumentImpl::~BoxArgumentImpl() {}

auto kratos::argument::BoxArgumentImpl::parse(int argc, const char **argv)
    -> bool {
  if (util::get_binary_name() == "box_client") {
    return parse_as_client(argc, argv);
  } else {
    return parse_as_server(argc, argv);
  }
}

auto kratos::argument::BoxArgumentImpl::get_config_file_path() const noexcept
    -> const std::string & {
  return config_file_path_;
}

auto kratos::argument::BoxArgumentImpl::get_config_file_name() const noexcept
    -> std::string {
  return util::get_file_name(config_file_path_);
}

auto kratos::argument::BoxArgumentImpl::get_max_frame() const noexcept -> int {
  return max_frame_;
}

auto kratos::argument::BoxArgumentImpl::is_print_help() const noexcept -> bool {
  return is_print_help_;
}

auto kratos::argument::BoxArgumentImpl::get_help_string() const noexcept
    -> std::string {
  return logo + get_version_string() + "\n" + help_string_;
}

auto kratos::argument::BoxArgumentImpl::get_command() const noexcept
    -> const std::string & {
  return command_;
}

auto kratos::argument::BoxArgumentImpl::get_host() const noexcept
    -> const std::string & {
  return host_;
}

auto kratos::argument::BoxArgumentImpl::is_daemon() const noexcept -> bool {
  return is_daemon_;
}

auto kratos::argument::BoxArgumentImpl::get_service_name() const noexcept
    -> const std::string & {
  return service_name_;
}

auto kratos::argument::BoxArgumentImpl::get_config_center_api_url()
    const noexcept
    -> const std::string & {
  return config_center_api_;
}

auto kratos::argument::BoxArgumentImpl::is_open_system_exception()
    const noexcept
    -> bool {
  return is_open_system_exception_;
}

auto kratos::argument::BoxArgumentImpl::is_proxy() const noexcept -> bool {
  return is_proxy_;
}

auto kratos::argument::BoxArgumentImpl::get_proxy_host() const noexcept
    -> std::string {
  return proxy_host_;
}

auto kratos::argument::BoxArgumentImpl::is_dump_heap() const noexcept -> bool {
  return is_dump_heap_;
}

auto kratos::argument::BoxArgumentImpl::get_logo() noexcept -> const char* {
  return logo;
}

auto kratos::argument::BoxArgumentImpl::on_change(const std::string& command,
  std::string& result) const -> bool {
  result.clear();
  std::vector<std::string> splitter;
  util::split(util::trim(command), " ", splitter);
  if (splitter.size() != 2) {
    result = "Invalid command format";
    return false;
  }
  result = "setting updated";
  const auto& cmd = splitter[0];
  const auto& value = splitter[1];
  auto& this_ref = const_cast<BoxArgumentImpl&>(*this);
  if ("max_frame" == cmd) {
    this_ref.max_frame_ = std::stoi(value);
  } else if ("is_open_system_exception" == cmd) {
    this_ref.is_open_system_exception_ = (value == "true" ? true : false);
  } else if ("is_dump_heap" == cmd) {
    this_ref.is_dump_heap_ = (value == "true" ? true : false);
  } else {
    result = "unknown setting";
  }
  return true;
}

auto kratos::argument::BoxArgumentImpl::set_max_frame(int max_frame) const noexcept -> void {
    const_cast<BoxArgumentImpl*>(this)->max_frame_ = max_frame;
}

auto kratos::argument::BoxArgumentImpl::parse_as_client(int argc,
                                                        const char **argv)
    -> bool {
  cxxopts::Options options("Service Box Client");
  options.add_options()
      ("c,config",              "Config file, .cfg",                                  cxxopts::value<std::string>())
      ("stop",                  "Stop service box",                                   cxxopts::value<bool>())
      ("reload",                "Reload config file",                                 cxxopts::value<bool>())
      ("proc_stat",             "Query process statistics",                           cxxopts::value<bool>())
      ("show-alloc",            "Query allocation statistics",                        cxxopts::value<bool>())
      ("show-sys-alloc",        "Query allocation statistics of service box ",        cxxopts::value<bool>())
      ("force-gc",              "Release all memory in pool ",                        cxxopts::value<bool>())
      ("show-config",           "Display configuration",                              cxxopts::value<bool>())
      ("host",                  "Remote service box command listener address",        cxxopts::value<std::string>())
      ("show-register-service", "Query all registered service",                       cxxopts::value<bool>())
      ("show-remote-service",   "Query all remote service interact with current box", cxxopts::value<bool>())
      ("h,help", "Help");
  try {
    auto result = options.parse(argc, argv);
    if (result["help"].count()) {
      is_print_help_ = true;
      help_string_ = options.help();
      return false;
    }
    if (result["stop"].count()) {
      command_ = "stop";
    } else if (result["reload"].count()) {
      command_ = "reload";
    } else if (result["show-config"].count()) {
      command_ = "show-config";
    } else if (result["proc_stat"].count()) {
      command_ = "proc_stat";
    } else if (result["show-alloc"].count()) {
      command_ = "show-alloc";
    } else if (result["show-register-service"].count()) {
      command_ = "show-register-service";
    } else if (result["show-remote-service"].count()) {
      command_ = "show-remote-service";
    } else if (result["show-sys-alloc"].count()) {
      command_ = "show-sys-alloc";
    } else if (result["force-gc"].count()) {
      command_ = "force-gc";
    }
    if (result["config"].count()) {
      config_file_path_ = result["config"].as<std::string>();
    }
    if (result["host"].count()) {
      host_ = result["host"].as<std::string>();
    }
    service_name_ = util::get_binary_name();
  } catch (std::exception &) {
    is_print_help_ = true;
    return false;
  }
  return true;
}

auto kratos::argument::BoxArgumentImpl::parse_as_server(int argc,
                                                        const char **argv)
    -> bool {
  cxxopts::Options options("Service Box");
#ifdef WIN32
  options.add_options()
      ("c,config",         "Config file, .cfg",                                                   cxxopts::value<std::string>())
      ("f,frame",          "Maximum frame count per second",                                      cxxopts::value<int>())
      ("alloc-dump",       "Dump the object in allocator after shutdown service box",             cxxopts::value<bool>())
      ("install",          "Install to SCM",                                                      cxxopts::value<bool>())
      ("uninstall",        "Uninstall from SCM",                                                  cxxopts::value<bool>())
      ("start_service",    "Notify SCM to start service",                                         cxxopts::value<bool>())
      ("stop_service",     "Notify SCM to stop service",                                          cxxopts::value<bool>())
      ("config-center",    "Remote config center API",                                            cxxopts::value<std::string>())
      ("system-exception", "Translating system error into C++ exception and try to keep running", cxxopts::value<bool>())
      ("proxy",            "Start as proxy",                                                      cxxopts::value<bool>())
      ("proxy-host",       "The proxy need to connect",                                           cxxopts::value<std::string>())
      ("h,help", "Help");
#else
  options.add_options()
      ("c,config",         "Config file, .cfg",                                                   cxxopts::value<std::string>())
      ("f,frame",          "Maximum frame count per second",                                      cxxopts::value<int>())
      ("alloc-dump",       "Dump the object in allocator after shutdown service box",             cxxopts::value<bool>())
      ("daemon",           "Start as daemon",                                                     cxxopts::value<bool>())
      ("config-center",    "Remote config center API",                                            cxxopts::value<std::string>())
      ("system-exception", "Translating system error into C++ exception and try to keep running", cxxopts::value<bool>())
      ("proxy",            "Start as proxy",                                                      cxxopts::value<bool>())
      ("proxy-host",       "The proxy need to connect",                                           cxxopts::value<std::string>())
      ("h,help", "Help");
#endif // WIN32
  try {
    auto result = options.parse(argc, argv);
    if (result["help"].count()) {
      is_print_help_ = true;
      help_string_ = options.help();
      return false;
    }
    if (result["config"].count()) {
      config_file_path_ = result["config"].as<std::string>();
      /*if (!util::is_path_exists(config_file_path_)) {
        throw std::runtime_error("Configuration file not found:" +
                                 config_file_path_);
        return false;
      }*/
    }
    if (result["frame"].count()) {
      max_frame_ = result["frame"].as<int>();
    }
    if (result["alloc-dump"].count()) {
      is_dump_heap_ = true;
    }
    #ifdef WIN32
    if (result["install"].count()) {
      command_ = "install";
    } else if (result["uninstall"].count()) {
      command_ = "uninstall";
    } else if (result["start_service"].count()) {
      command_ = "start_service";
    } else if (result["stop_service"].count()) {
      command_ = "stop_service";
    }
    #else
    if (result["daemon"].count()) {
      is_daemon_ = true;
    }
    #endif // WIN32
    service_name_ = util::get_binary_name();
    if (result["config-center"].count()) {
      config_center_api_ = result["config-center"].as<std::string>();
    }
    if (result["system-exception"].count()) {
      is_open_system_exception_ = true;
    }
    if (result["proxy"].count()) {
      is_proxy_ = true;
    }
    if (result["proxy-host"].count()) {
      proxy_host_ = result["proxy-host"].as<std::string>();
    }
  } catch (std::exception &) {
    is_print_help_ = true;
    return false;
  }
  return true;
}
